﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace Moon.Sql
{
    public abstract partial class Repository<TEntity, TKey> : IRepository<TEntity, TKey> where TEntity : class
    {
        private IMoonSql Context = null;
        public MapperTable MapperTable = null;

        protected Repository(IMoonSql context)
        {
            MapperTable = ClassMapper.Mapping<TEntity>();
            this.Context = context;
        }

        public IInsertable<TEntity> GetInsertable(TEntity entity)
        {
            return Context.Insertable<TEntity>(entity);
        }

        public IInsertable<TEntity> GetInsertable(TEntity entity, string tableName)
        {
            return Context.Insertable<TEntity>(entity).TableName(tableName);
        }

        public IInsertable<TEntity> GetInsertable(List<TEntity> list)
        {
            return Context.Insertable<TEntity>(list);
        }

        public IInsertable<TEntity> GetInsertable(List<TEntity> list, string tableName)
        {
            return Context.Insertable<TEntity>(list).TableName(tableName);
        }

        public IUpdateable<TEntity> GetUpdateable(TEntity entity)
        {
            return Context.Updateable<TEntity>(entity);
        }

        public IDeleteable<TEntity> GetDeleteable()
        {
            return Context.Deleteable<TEntity>();
        }

        public IDeleteable<TEntity> GetDeleteable(params TKey[] pks)
        {
            return Context.Deleteable<TEntity>(pks);
        }

        public IQueryable<TEntity> GetQueryable()
        {
            return Context.Queryable<TEntity>();
        }

        public int Insert(TEntity entity)
        {
            return Context.Insertable<TEntity>(entity).Execute();
        }

        public int Insert(TEntity entity, string tableName)
        {
            return Context.Insertable<TEntity>(entity).TableName(tableName).Execute();
        }

        public int Insert(List<TEntity> list)
        {
            return Context.Insertable<TEntity>(list).Execute();
        }

        public int Insert(List<TEntity> list, string tableName)
        {
            return Context.Insertable<TEntity>(list).TableName(tableName).Execute();
        }

        public int BulkInsert(List<TEntity> list)
        {
            return Context.Insertable<TEntity>(list).BulkInsert();
        }

        public int BulkInsert(List<TEntity> list, string tableName)
        {
            return Context.Insertable<TEntity>(list).TableName(tableName).BulkInsert();
        }

        public long InsertGetIdentity(TEntity entity)
        {
            return Context.Insertable<TEntity>(entity).ExecuteIdentity();
        }

        public long InsertGetIdentity(TEntity entity, string tableName)
        {
            return Context.Insertable<TEntity>(entity).TableName(tableName).ExecuteIdentity();
        }

        public int Delete(params TKey[] pks)
        {
            return Context.Deleteable<TEntity>(pks).Execute();
        }

        public int Delete(TEntity entity)
        {
            var array = MapperTable.Properties.Where(i => i.IsPrimaryKey);
            if (array != null && array.Count() == 1)
            {
                PropertyMap propertyMap = array.FirstOrDefault();
                return Context.Deleteable<TEntity>(propertyMap.PropertyInfo.GetValue(entity)).Execute();
            }
            else
            {
                throw new Exception("primary key is empty or has more than one primary key");
            }
        }

        public int Delete(Expression<Func<TEntity, bool>> where)
        {
            return Context.Deleteable<TEntity>().Where(where).Execute();
        }

        //public int Update(TEntity entity)
        //{
        //    return Context.Updateable<TEntity>(entity).Where().Execute();
        //}

        public int Update(TEntity entity, Expression<Func<TEntity, bool>> where)
        {
            return Context.Updateable<TEntity>(entity).Where(where).Execute();
        }

        public int Update(TEntity entity, Expression<Func<TEntity, object>> columns, Expression<Func<TEntity, bool>> where, Expression<Func<TEntity, object>> ignoreColumns = null)
        {
            return Context.Updateable<TEntity>(entity).SetColumns(columns).IgnoreColumns(ignoreColumns).Where(where).Execute();
        }

        public List<TEntity> GetList()
        {
            return Context.Queryable<TEntity>().ToList();
        }

        public List<TEntity> GetList(string tableName)
        {
            return Context.Queryable<TEntity>().TableName(tableName).ToList();
        }

        public List<TEntity> GetList(Expression<Func<TEntity, bool>> where)
        {
            return Context.Queryable<TEntity>().Where(where).ToList();
        }

        public List<TEntity> GetList(Expression<Func<TEntity, bool>> where, string tableName)
        {
            return Context.Queryable<TEntity>().Where(where).TableName(tableName).ToList();
        }

        public QueryPageResult<TEntity> GetPageList(Expression<Func<TEntity, bool>> where, int offset, int limit)
        {
            return Context.Queryable<TEntity>().Where(where).ToPageList<TEntity>(offset, limit);
        }

        public QueryPageResult<TEntity> GetPageList(Expression<Func<TEntity, bool>> where, int offset, int limit, Expression<Func<TEntity, object>> orderField, OrderBy orderBy = Moon.Sql.OrderBy.Asc)
        {
            return Context.Queryable<TEntity>().Where(where).OrderBy(orderField).ToPageList<TEntity>(offset, limit);
        }

        public int DeleteById(TKey id)
        {
            return Context.Deleteable<TEntity>(id).Execute();
        }

        public TEntity GetById(TKey id)
        {
            return Context.Queryable<TEntity>().First(id);
        }

        public TEntity GetById(TKey id, string tableName)
        {
            return Context.Queryable<TEntity>().TableName(tableName).First(id);
        }

        public TEntity FirstOrDefault(Expression<Func<TEntity, bool>> where)
        {
            return Context.Queryable<TEntity>().Where(where).First();
        }

        public TEntity FirstOrDefault(Expression<Func<TEntity, bool>> where, string tableName)
        {
            return Context.Queryable<TEntity>().TableName(tableName).Where(where).First();
        }

        //~Repository() => this.Dispose();

        public void Dispose()
        {
            //Context.Dispose();
        }
    }
}
